package com.jl;

import lombok.SneakyThrows;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * bean工具
 */
public class BeanUtils {

    /**
     * 对象拷贝
     *
     * @param resultClass 返回对象class
     * @param dataEntity  数据对象
     * @param <T>
     * @return
     */
    @SneakyThrows
    public static <T> T copy(Class<T> resultClass, Object dataEntity) {
        if (dataEntity == null) {
            //避免空指针问题
            return resultClass.newInstance();
        }
        return copy(resultClass.newInstance(), dataEntity);
    }

    /**
     * 对象拷贝
     *
     * @param resultEntity 返回对象
     * @param dataEntity   数据对象
     * @param <T>
     * @return
     */
    public static <T> T copy(T resultEntity, Object dataEntity) {
        List<Tuple.Tuple3<String, Object, Class<?>>> resultProperty = ReflectUtils.PropertyReflect.getProperty(resultEntity);
        Map<String, Tuple.Tuple3<String, Object, Class<?>>> resultPropertyMap = resultProperty.stream().collect(Collectors.toMap(o -> o.getV1(), Function.identity(), (key1, key2) -> key2));
        List<Tuple.Tuple3<String, Object, Class<?>>> dataProperty = ReflectUtils.PropertyReflect.getProperty(dataEntity);
        Map<String, List<Tuple.Tuple3<String, Object, Class<?>>>> dataPropertyMap = dataProperty.stream().collect(Collectors.groupingBy(o -> o.getV1()));
        resultPropertyMap.forEach((resultName, tuple3) -> {
            List<Tuple.Tuple3<String, Object, Class<?>>> tuple3s = dataPropertyMap.get(resultName);
            if (!EmptyUtils.check(tuple3s)) {
                return;
            }
            for (Tuple.Tuple3<String, Object, Class<?>> dataTuple3 : tuple3s) {
                if (dataTuple3 == null || !EmptyUtils.check(dataTuple3.getV2())) {
                    continue;
                }
                Object value = dataTuple3.getV2();
                char[] cs = dataTuple3.getV1().toCharArray();
                cs[0] -= 32;
                Method method = ReflectUtils.MethodReflect.getMethod(resultEntity.getClass(), "set" + String.valueOf(cs), tuple3.getV3());
                Object[] param = dataTuple3.getV3().equals(tuple3.getV3()) ? new Object[]{value} : ReflectUtils.MethodReflect.checkParamType(method.getParameters(), new Object[]{value});
                try {
                    method.invoke(resultEntity, param);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return;
            }
        });
        return resultEntity;
    }

    /**
     * 集合拷贝
     *
     * @param resultClass 返回集合泛型class
     * @param dataList    数据集合
     * @param <T>         泛型
     * @return
     */
    public static <T> List<T> copy(Class<T> resultClass, List<?> dataList) {
        List<T> result = new ArrayList<>(dataList.size());
        for (Object o : dataList) {
            result.add(copy(resultClass, o));
        }
        return result;
    }

    /**
     * 递归生成树
     *
     * @param father      最顶层父级
     * @param entities    全部数据集合
     * @param fatherCode  父级关联子级属性名
     * @param sonCode     子级关联父级属性名
     * @param sonListCode 存放子级集合属性名
     * @param <T>
     * @return
     */
    public static <T> List<T> tree(T father, List<T> entities, LambdaUtils.JFunction<T, ?> fatherCode, LambdaUtils.JFunction<T, ?> sonCode, LambdaUtils.JFunction<T, ?> sonListCode) {
        List<T> tree = new ArrayList<>();
        //父属性值
        String fatherProperty = LambdaUtils.getProperty(fatherCode);
        Object fatherValue = ReflectUtils.PropertyReflect.getProperty(father, fatherProperty).getV2();
        //子属性名称
        String sonProperty = LambdaUtils.getProperty(sonCode);
        //子集合set方法名
        String sonListProperty = String.format("s%s", LambdaUtils.getMethod(sonListCode).substring(1));
        for (T entity : entities) {
            //子属性值
            Object sonValue = ReflectUtils.PropertyReflect.getProperty(entity, sonProperty).getV2();
            if (sonValue != null && sonValue.toString().equals(fatherValue.toString())) {
                tree.add(entity);
                ReflectUtils.MethodReflect.getMethodValue(entity, sonListProperty, tree(entity, entities, fatherCode, sonCode, sonListCode));
            }
        }
        return tree;
    }

    /**
     * 集合外键关系拷贝
     *
     * @param sourceObj  源数据集合
     * @param targetObj  目标数据集合
     * @param sourceCode 源关联属性
     * @param targetCode 目标关联属性
     * @param sourceProp 源接受属性值
     * @param targetProp 目标属性值
     * @param <S>
     * @param <T>
     */
    public static <S, T> void copy(CopyFunction<S> sourceObj, CopyFunction<T> targetObj, LambdaUtils.JFunction<S, ?> sourceCode, LambdaUtils.JFunction<T, ?> targetCode, LambdaUtils.JFunction<S, ?> sourceProp, LambdaUtils.JFunction<T, ?> targetProp) {
        String sourceCodeProperty = LambdaUtils.getProperty(sourceCode);
        String targetCodeProperty = LambdaUtils.getProperty(targetCode);
        String sourcePropProperty = LambdaUtils.getProperty(sourceProp);
        String targetPropProperty = LambdaUtils.getProperty(targetProp);
        Map<String, T> targetMap = targetObj.exec().stream().collect(Collectors.toMap(o -> {
            try {
                Tuple.Tuple3<String, Object, Class<?>> tuple3 = ReflectUtils.PropertyReflect.getProperty(o, targetCodeProperty);
                if (tuple3 == null) {
                    return null;
                }
                return tuple3.getV2().toString();
            } catch (Exception e) {
            }
            return null;
        }, Function.identity(), (key1, key2) -> key2));
        for (S source : sourceObj.exec()) {
            Tuple.Tuple3<String, Object, Class<?>> sourceTuple3 = ReflectUtils.PropertyReflect.getProperty(source, sourceCodeProperty);
            Object sourceCodeValue = sourceTuple3.getV2();
            if (!EmptyUtils.check(sourceCodeValue)) {
                continue;
            }
            T target = targetMap.get(sourceCodeValue.toString());
            if (!EmptyUtils.check(target)) {
                continue;
            }
            Tuple.Tuple3<String, Object, Class<?>> targetTuple3 = ReflectUtils.PropertyReflect.getProperty(target, targetPropProperty);
            Object targetPropValue = targetTuple3.getV2();
            if (!EmptyUtils.check(targetPropValue)) {
                continue;
            }
            ReflectUtils.PropertyReflect.setPropertyValue(source, sourcePropProperty, targetPropValue);
        }
    }

    /**
     * 集合外键关系拷贝
     *
     * @param sourceObj  源数据集合
     * @param targetObj  目标数据集合
     * @param sourceCode 源关联属性
     * @param targetCode 目标关联属性
     * @param sourceProp 源接受属性值
     * @param targetProp 目标属性值
     * @param <S>
     * @param <T>
     */
    public static <S, T> void copy(List<S> sourceObj, CopyFunction<T> targetObj, LambdaUtils.JFunction<S, ?> sourceCode, LambdaUtils.JFunction<T, ?> targetCode, LambdaUtils.JFunction<S, ?> sourceProp, LambdaUtils.JFunction<T, ?> targetProp) {
        copy(() -> sourceObj, targetObj, sourceCode, targetCode, sourceProp, targetProp);
    }

    /**
     * 集合外键关系拷贝
     *
     * @param sourceObj  源数据集合
     * @param targetObj  目标数据集合
     * @param sourceCode 源关联属性
     * @param targetCode 目标关联属性
     * @param sourceProp 源接受属性值
     * @param targetProp 目标属性值
     * @param <S>
     * @param <T>
     */
    public static <S, T> void copy(CopyFunction<S> sourceObj, List<T> targetObj, LambdaUtils.JFunction<S, ?> sourceCode, LambdaUtils.JFunction<T, ?> targetCode, LambdaUtils.JFunction<S, ?> sourceProp, LambdaUtils.JFunction<T, ?> targetProp) {
        copy(sourceObj, () -> targetObj, sourceCode, targetCode, sourceProp, targetProp);
    }

    /**
     * 集合外键关系拷贝
     *
     * @param sourceObj  源数据集合
     * @param targetObj  目标数据集合
     * @param sourceCode 源关联属性
     * @param targetCode 目标关联属性
     * @param sourceProp 源接受属性值
     * @param targetProp 目标属性值
     * @param <S>
     * @param <T>
     */
    public static <S, T> void copy(List<S> sourceObj, List<T> targetObj, LambdaUtils.JFunction<S, ?> sourceCode, LambdaUtils.JFunction<T, ?> targetCode, LambdaUtils.JFunction<S, ?> sourceProp, LambdaUtils.JFunction<T, ?> targetProp) {
        copy(() -> sourceObj, () -> targetObj, sourceCode, targetCode, sourceProp, targetProp);
    }

    @FunctionalInterface
    public interface CopyFunction<T> {
        List<T> exec();
    }

}
